import os
import re
import json


class Model:
    """
    this class store leapp model data, contain:
        - model name: string
        - produced actors' name list: string list
        - consumed actors' name list: string list
    """
    def __init__(self, name: str):
        self.name = name
        self.produced_actors = []
        self.consumed_actors = []

    def to_dict(self):
        return {
            'name': self.name,
            'produced_actors': [actor.name for actor in self.produced_actors],
            'consumed_actors': [actor.name for actor in self.consumed_actors]
        }


class Actor:
    """
    this class store leapp actor data, contain:
        - actor name: string
        - phase name: string
        - index: int
    """
    def __init__(self, name: str, phase_name: str, index: int):
        self.name = name
        self.phase_name = phase_name
        self.index = index
        self.produce_models = []
        self.consume_models = []

    def to_dict(self):
        return {
            'name': self.name,
            'phase_name': self.phase_name,
            'produce_models': [model.name for model in self.produce_models],
            'consume_models': [model.name for model in self.consume_models]
        }


class Phase:
    """
    this class store leapp phase data, contain:
        - phase name: string
        - index: int
        - actor list: Actor list
    """
    def __init__(self, name: str, index: int):
        self.name = name
        self.index = index
        self.actors = []

    def to_dict(self):
        return {
            'name': self.name,
            'actors': [actor.name for actor in self.actors]
        }


class Scan:
    """
    this class store scan leapp-repository/repos/system_upgrade/common/actors
    and leapp-repository/repos/system_upgrade/el7toel8/actors class files to get all actors
    data and record model and phase data
    """
    def __init__(self):
        self.actors = []
        self.models = {}
        self.phases = []
        self.base_path = ""

    def scan(self, base_path: str):
        self.base_path = base_path
        for dirpath, dirnames, filenames in os.walk(self.base_path):
            for filename in filenames:
                if filename == "actor.py":
                    self.parse_actor(os.path.join(dirpath, filename))

    def parse_actor(self, file_path: str):
        with open(file_path, 'r') as f:
            content = f.read()

        # 提取 Actor 名称、消费的 Model 和产生的 Model
        actor_name = re.search(r'class\s+(\w+)\(Actor\):', content)
        consumes = re.search(r'consumes\s*=\s*\(([\w,. \n]+)\)', content)
        produces = re.search(r'produces\s*=\s*\(([\w,. \n]+)\)', content)

        if not actor_name:
            return

        actor_name = actor_name.group(1)
        consumes = [c.strip() for c in consumes.group(1).split(',') if c.strip()] if consumes and consumes.group(1) else []
        produces = [p.strip() for p in produces.group(1).split(',') if p.strip()] if produces and produces.group(1) else []

        # 提取 Phase 信息
        phase = self.parse_phase(content)
        if not phase:
            # IPUWorkflowConfig 没有 Phase 信息，就不处理了
            # print(actor_name)
            return

        # 将提取的信息添加到相应的数据结构中
        actor = Actor(actor_name, phase.name, len(self.actors))
        self.actors.append(actor)
        phase.actors.append(actor)

        for model_name in consumes:
            if model_name not in self.models:
                self.models[model_name] = Model(model_name)
            model = self.models[model_name]
            actor.consume_models.append(model)
            model.consumed_actors.append(actor)

        for model_name in produces:
            if model_name not in self.models:
                self.models[model_name] = Model(model_name)
            model = self.models[model_name]
            actor.produce_models.append(model)
            model.produced_actors.append(actor)

    def parse_phase(self, content: str) -> Phase:
        phase = re.search(r'tags\s*=\s*\(([\w,.\s]+)\)', content)
        if phase:
            phases = [p.strip() for p in phase.group(1).split(',')]
            for p in phases:
                # RemoveSystemdResumeService 的 PhaseTag 是 FirstBootPhaseTag.After，得特殊处理
                p = re.search(r'(\w+Phase)Tag', p)
                if p:
                    phase_name = p.group(1)
                    for existing_phase in self.phases:
                        if existing_phase.name == phase_name:
                            return existing_phase
                    new_phase = Phase(phase_name, len(self.phases))
                    self.phases.append(new_phase)
                    return new_phase
        return None


if __name__ == '__main__':
    scan = Scan()
    # scan.scan(r'D:\Code\work\python\ActorVis\utils\data')
    scan.scan(r'D:\Work\leapp-repository\repos\system_upgrade\common\actors')
    scan.scan(r'D:\Work\leapp-repository\repos\system_upgrade\el7toel8\actors')

    # 输出扫描结果
    # print("Actors:")
    # for actor in scan.actors:
    #     print(f" - {actor.name} (Phase: {actor.phase_name}, Index: {actor.index})")
    #
    # print("\nModels:")
    # for model in scan.models.values():
    #     print(f" - {model.name} (Produced by: {[a.name for a in model.produced_actors]}, "
    #           f"Consumed by: {[a.name for a in model.consumed_actors]})")
    #
    # print("\nPhases:")
    # for phase in sorted(scan.phases, key=lambda p: p.index):
    #     print(f" - {phase.name} (Index: {phase.index}, Actors: {[a.name for a in phase.actors]})")

    print(len(scan.actors) + len(scan.models))

    with open("actors.json", 'w') as f:
        json.dump([actor.to_dict() for actor in scan.actors], f, indent=4)

    with open("models.json", 'w') as f:
        json.dump([model.to_dict() for model in scan.models.values()], f, indent=4)

    with open("phases.json", 'w') as f:
        json.dump([phase.to_dict() for phase in scan.phases], f, indent=4)

